package common;

import java.io.File;
import java.util.Date;
import java.util.Timer;
import java.util.TimingTask;

import org.apache.log4j.PropertyConfigurator;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.serial.SerialAddress;
import org.apache.mina.transport.serial.SerialConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.jdbc.JdbcConnectionSource;
import com.j256.ormlite.table.DatabaseTable;
import com.j256.ormlite.table.TableUtils;
import com.szwistar.common.comm.ModbusRtuAck;
import com.szwistar.common.comm.ModbusRtuCodec;
import com.szwistar.common.comm.ModbusRtuFrame;
import com.szwistar.common.comm.SerialAddressEx;


class StringEchoServerHandler implements IoHandler {
	protected static Logger LOG = LoggerFactory.getLogger(StringEchoServerHandler.class);
	protected static Logger LOGCOMM = LoggerFactory.getLogger("comm");

	@Override
	public void sessionCreated(IoSession session) throws Exception {
		LOG.debug("sessionCreated({}): {} -> {}", session.getId(),
				session.getLocalAddress(),
				session.getRemoteAddress());
	}

	@Override
	public void sessionOpened(IoSession session) throws Exception {
		LOG.debug("sessionOpened({}): {} -> {}", session.getId(),
				session.getLocalAddress(),
				session.getRemoteAddress());
	}

	@Override
	public void sessionClosed(IoSession session) throws Exception {
		LOG.debug("sessionClosed({}): {}", session.getId(),
				session.getRemoteAddress());
	}

	@Override
	public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
		LOG.debug("sessionIdle({})", session.getId());
	}

	@Override
	public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
		LOG.debug("exceptionCaught({}): {}", session.getId(), cause.toString());
	}

	@Override
	public void messageSent(IoSession session, Object message) throws Exception {
		//LOG.debug("messageSent({}): {}", session.getId(), message.toString());
	}

	@Override
	public void messageReceived(IoSession session, Object message) throws Exception {
		ModbusRtuFrame msg = (ModbusRtuFrame)message;
		if(msg.shorts.length == 31) {
			Rs4041Frame frame = new Rs4041Frame(msg);
			Rs4041Frame.dao.create(frame);
			LOGCOMM.debug("{}", frame);
		} else if(msg.shorts.length == 1) {
			Rs4012wFrame frame = new Rs4012wFrame(msg);
			Rs4012wFrame.dao.create(frame);
			LOGCOMM.debug("{}", frame);
		} else if(msg.shorts.length == 4) {
			Ws4Frame frame = new Ws4Frame(msg);
			Ws4Frame.dao.create(frame);
			LOGCOMM.debug("{}", frame);
		}
	}
}

@DatabaseTable(tableName = "rs4041")
class Rs4041Frame {
	// DAO
	static Dao dao;

	// 设备地址
	@DatabaseField protected int addr;
	// 时间戳
	@DatabaseField protected Date time;

	//                          寄存器内容	寄存器个数	寄存器状态	数据范围
	@DatabaseField protected float Va;     // VA相电压	1	只读	0~12000
	@DatabaseField protected float Vb;     // VB相电压	1	只读	0~12000
	@DatabaseField protected float Vc;     // VC相电压	1	只读	0~12000
	@DatabaseField protected float Ia;     // IA相电流	1	只读	0~12000
	@DatabaseField protected float Ib;     // IB相电流	1	只读	0~12000
	@DatabaseField protected float Ic;     // IC相电流	1	只读	0~12000
	@DatabaseField protected float Pa;     // PA相有功功率	1	只读	-12000~+12000
	@DatabaseField protected float Pb;     // PB相有功功率	1	只读	-12000~+12000
	@DatabaseField protected float Pc;     // PC相有功功率	1	只读	-12000~+12000
	@DatabaseField protected float COSa;   // COSA相功率因数	1	只读	-10000~+10000
	@DatabaseField protected float COSb;   // COSB相功率因数	1	只读	-10000~+10000
	@DatabaseField protected float COSc;   // COSC相功率因数	1	只读	-10000~+10000
	@DatabaseField protected float Pt;     // Pt: P总有功功率	1	只读	-12000~+12000
	@DatabaseField protected float Qt;     // Qt: Q总无功功率	1	只读	-12000~+12000
	@DatabaseField protected float Ft;     // Ft: 三相平均功率因数	1	只读	-10000~+10000
	@DatabaseField protected float Freq;   // Freq: F频率	1	只读	4500~65000
	@DatabaseField protected float Qpd;    // 正向有功电度	2	只读	0~7FFFFFFFH
	@DatabaseField protected float Qqd;    // 正向无功电度	2	只读	0~7FFFFFFFH
	@DatabaseField protected float Qpr;    // 反向有功电度	2	只读	0~7FFFFFFFH
	@DatabaseField protected float Qqr;    // 反向无功电度	2	只读	0~7FFFFFFFH
	@DatabaseField protected float Qa;     // QA无功功率	1	只读	-12000~+12000
	@DatabaseField protected float Qb;     // QB无功功率	1	只读	-12000~+12000
	@DatabaseField protected float Qc;     // QC无功功率	1	只读	-12000~+12000
	@DatabaseField protected float Sa;     // SA视在功率	1	只读	0~12000
	@DatabaseField protected float Sb;     // SB视在功率	1	只读	0~12000
	@DatabaseField protected float Sc;     // SC视在功率	1	只读	0~12000
	@DatabaseField protected float St;     // S总视在功率	1	只读	0~12000

	public Rs4041Frame() {}

	public Rs4041Frame(ModbusRtuAck frame) {
		addr = frame.dst;
		time = new Date();
		Va   = frame.shorts[ 0]*0.025f; Vb   = frame.shorts[ 1]*0.025f; Vc   = frame.shorts[ 2]*0.025f;
		Ia   = frame.shorts[ 3]*0.060f; Ib   = frame.shorts[ 4]*0.060f; Ic   = frame.shorts[ 5]*0.060f;
		Pa   = frame.shorts[ 6]*15f   ; Pb   = frame.shorts[ 7]*15f   ; Pc   = frame.shorts[ 8]*15f   ;
		COSa = frame.shorts[ 9]/10000f; COSb = frame.shorts[10]/10000f; COSc = frame.shorts[11]/10000f;
		Pt   = frame.shorts[12]*45f   ; Qt   = frame.shorts[13]*45f   ;
		Ft   = frame.shorts[14]/10000f;
		Freq = frame.shorts[15]*0.01f ;
        Qpd  = (frame.shorts[16]<<16 | frame.shorts[17])*0.042f;
        Qqd  = (frame.shorts[18]<<16 | frame.shorts[19])*0.042f;
        Qpr  = (frame.shorts[20]<<16 | frame.shorts[21])*0.042f;
        Qqr  = (frame.shorts[22]<<16 | frame.shorts[23])*0.042f;
		Qa   = frame.shorts[24]*15f   ; Qb   = frame.shorts[25]*15f   ; Qc   = frame.shorts[26]*15f   ;
		Sa   = frame.shorts[27]*15f   ; Sb   = frame.shorts[28]*15f   ; Sc   = frame.shorts[29]*15f   ;
		St   = frame.shorts[30]*45f   ;
	}

	@Override
	public String toString() {
		return String.format("Rs4041Frame: \r\n"+
				"Va=%8.3fV, Vb=%8.3fV, Vc=%8.3fV\r\n"+
				"Ia=%8.3fA, Ib=%8.3fA, Ic=%8.3fA\r\n"+
				"Pa=%8.3fW, Pb=%8.3fW, Pc=%8.3fW\r\n"+
				"COSa=%8.3f, COSb=%8.3f, COSc=%8.3f\r\n"+
				"Pt=%8.3fW, Qt=%8.3fW, Ft=%8.3f %%, Freq=%8.3f Hz\r\n"+
				"Ppd=%8.3fW, Pqd=%8.3fW, Ppr=%8.3fW, Pqr=%8.3fW\r\n"+
				"Qa=%8.3fW, Qb=%8.3fW, Qc=%8.3fW\r\n"+
				"Sa=%8.3fW, Sb=%8.3fW, Sc=%8.3fW, St=%8.3fW\r\n",
				Va, Vb, Vc,
				Ia, Ib, Ic,
				Pa, Pb, Pc,
				COSa, COSb, COSc,
				Pt, Qt, Ft, Freq,
				Qpd, Qqd, Qpr, Qqr,
				Qa, Qb, Qc,
				Sa, Sb, Sc, St
		);
	}
}

@DatabaseTable(tableName = "rs4012w")
class Rs4012wFrame {
	// DAO
	static Dao dao;

	// 设备地址
	@DatabaseField protected int addr;
	// 时间戳
	@DatabaseField protected Date time;

	// 剩余电流
	@DatabaseField protected float rc;

	public Rs4012wFrame() {}

	public Rs4012wFrame(ModbusRtuFrame frame) {
		addr = frame.dst;
		time = new Date();
		rc   = frame.shorts[ 0]/10000f;
	}

	@Override
	public String toString() {
		return String.format("Rs4012wFrame: rc=%8.3f", rc);
	}
}

@DatabaseTable(tableName = "ws4")
class Ws4Frame {
	// DAO
	static Dao dao;

	// 设备地址
	@DatabaseField protected int addr;
	// 时间戳
	@DatabaseField protected Date time;

	// 温度1
	@DatabaseField protected float temp1;
	// 温度2
	@DatabaseField protected float temp2;
	// 温度3
	@DatabaseField protected float temp3;
	// 温度4
	@DatabaseField protected float temp4;

	public Ws4Frame() {}

	public Ws4Frame(ModbusRtuFrame frame) {
		addr = frame.dst;
		time = new Date();
		temp1 = frame.shorts[ 0]*0.1f;
		temp2 = frame.shorts[ 1]*0.1f;
		temp3 = frame.shorts[ 2]*0.1f;
		temp4 = frame.shorts[ 3]*0.1f;
	}

	@Override
	public String toString() {
		return String.format("Ws4Frame: temp1=%8.3f, temp2=%8.3f, temp3=%8.3f, temp4=%8.3f", temp1, temp2, temp3, temp4);
	}
}

public class ModbusRtuTest {
	protected static Logger LOG = LoggerFactory.getLogger(ModbusRtuTest.class);
	protected static Logger LOGCOMM = LoggerFactory.getLogger("comm");

	// 串口连接
	static IoSession conn = null;

	/**
	 * 主程序
	 * @param args "COM2:9600,8,n,1" 1 2 3 4
	 * @throws Exception
	 */
    public static void main(String[] args) throws Exception {
		// 加载日志配置文件（放在jar同级的conf目录下）
		PropertyConfigurator.configure("log4j.properties");

		short b1 = 0x1883;
		short b2 = 0x0001;
		int d = (b2<<16 | b1);
		float f = d*0.042f;

        // 打开串口，并开始数据采集
        if(args.length < 5) {
        	System.out.println("java -jar rs4041.jar \"COM2:9600,8,n,1\" 1 2 3 4");
        	return;
        }

        String dbname = "rs4041.db";
        File dbfile = new File(dbname);
        boolean initDb = !dbfile.exists();
    	String connectionString = "jdbc:sqlite:rs4041.db";
    	JdbcConnectionSource dbconn = new JdbcConnectionSource(connectionString);
    	Rs4041Frame.dao = DaoManager.createDao(dbconn, Rs4041Frame.class);
    	Rs4012wFrame.dao = DaoManager.createDao(dbconn, Rs4012wFrame.class);
    	Ws4Frame.dao = DaoManager.createDao(dbconn, Ws4Frame.class);

        if(initDb) {
    		LOG.info("数据库文件 {} 不存在，将会新建工程！", dbfile.getCanonicalPath());
    		TableUtils.createTable(Rs4041Frame.dao);
    		TableUtils.createTable(Rs4012wFrame.dao);
    		TableUtils.createTable(Ws4Frame.dao);
        }

        int addrs[] = new int[args.length-1];
        for(int i=0; i<args.length-1; i++) {
        	addrs[i] = Integer.parseInt(args[i+1]);
        }
		test(args[0], addrs);
    }

    public static void test(String address, final int addrs[]) throws Exception {
		// 创建串口连接器
		IoConnector connector = new SerialConnector();
        // 设置编码器
		connector.getFilterChain().addLast("codec", ModbusRtuCodec.newFilter());
        // 设置Handler
		connector.setHandler(new StringEchoServerHandler());
		// 设置串口通信参数
		SerialAddress portAddress = SerialAddressEx.fromString(address);

		// 开始连接
        LOG.debug("启动 MODBUS-RTU 测试({}) ...", portAddress);
        ConnectFuture future = connector.connect(portAddress);

        ModbusRtuFrame frame1 = new ModbusRtuFrame((byte)addrs[0], (byte)3, (short)0, (short)31, null, null);
        ModbusRtuFrame frame2 = new ModbusRtuFrame((byte)addrs[1], (byte)3, (short)0, (short)31, null, null);
        ModbusRtuFrame frame3 = new ModbusRtuFrame((byte)addrs[2], (byte)3, (short)1, (short)1, null, null);
        ModbusRtuFrame frame4 = new ModbusRtuFrame((byte)addrs[3], (byte)3, (short)0x0190, (short)4, null, null);

		try {
			// 等待连接完成
			future.await();
			conn = future.getSession();
		} catch (Exception e) {
			LOG.error("打开串口失败：{}", portAddress, e);
		}

		// 启动任务
		new Timer("timmingReport").schedule(new TimingTask() {
			@Override
			public void run() {
				try {
					if(conn == null || !conn.isConnected()) { return; }
					conn.write(frame1); Thread.sleep(200);
					conn.write(frame2); Thread.sleep(200);
					conn.write(frame3); //Thread.sleep(500);
					//conn.write(frame4);
				} catch (Exception e) {
					LOG.error("发送查询指令失败！", e);
				}
			}
		}, 0, 5000);
    }
}
